package org.mbds.sensorsdatacollector.android.tools;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;

import android.content.Context;
import android.location.Address;
import android.location.Criteria;
import android.location.Geocoder;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;

/**
 * LocationPoint tools
 * @author VLWB5512
 *
 */
public abstract class GPSTools {
	private static String m_BestProvider = null; 
	private static LocationManager m_LocationManager = null; 
	private static LocationListener m_LocationListenerGPS = null; 
	private static Location m_Location = null; 
	private static ArrayList<LocationListener> listenersGPS = new ArrayList<LocationListener>();
	public final static int RADIUS = 6371;
	
	/**
	 * Coordinates in degrees/minutes/seconds from decimal latitude or longitude
	 * @param dec
	 * @return
	 */
	public static int[] decToDms(double dec) {
		int[] result = new int[3];
		result[0] = (int)Math.round(Math.abs(dec));
		if (dec<0)
			result[0]=-result[0];
		double temp = dec-result[0];
		temp *=3600;
		result[1] = (int) Math.floor(temp / 60);
		result[2] = (int) temp - result[1]*60;
		return result;
	}
	/**
	 * Distance in meters between 2 points
	 * @param latitude1
	 * @param longitude1
	 * @param latitude2
	 * @param longitude2
	 * @return
	 */
	public static double calcDistanceMeters(double latitude1,double longitude1,
			double latitude2, double longitude2) {
		  double distance = 2*Math.asin(Math.sqrt(Math.pow((Math.sin(Math.toRadians(latitude1)-Math.toRadians(latitude2))/2),2)
                          + Math.cos(Math.toRadians(latitude1))*Math.cos(Math.toRadians(latitude2))
                          *Math.pow(Math.sin((Math.toRadians(longitude1)-Math.toRadians(longitude2))/2),2)))
                          *RADIUS*1000;
		  distance = Math.abs(distance);//unsigned

		  return distance;
	}

	/**
	 * Distance in kilometers between 2 points
	 * @param latitude1
	 * @param longitude1
	 * @param latitude2
	 * @param longitude2
	 * @return
	 */
	public static double calcDistanceKilometers(double latitude1,double longitude1,
			double latitude2, double longitude2) {
		  if (latitude1==latitude2 && longitude1==longitude2) 
			  return 0;
		  double distance = Math.acos((Math.sin(Math.toRadians(latitude1))*Math.sin(Math.toRadians(latitude2)))
				  +(Math.cos(Math.toRadians(latitude1))*Math.cos(Math.toRadians(latitude2))
						  *Math.cos(Math.toRadians(longitude2)-Math.toRadians(longitude1))))*RADIUS;

		  distance = Math.abs(Math.round(distance));
		  if (distance>0) 
			  // Rounded 2 dec
			  return Math.round(distance)+(distance*100-Math.round(distance*100))/100;
		  return 0;
	}
	/**
	 * Find address from String address
	 * @param strAddress
	 * @return
	 */
	public static Address getLocationFromStrAdress(String strAddress, Context context) {
		Geocoder coder = new Geocoder(context); 
		List<Address> address = null; 
		 
	    try {
			address = coder.getFromLocationName(strAddress,5);
		} catch (IOException e) {
			e.printStackTrace();
		} 
	    if (address == null || address.size()==0) { 
	        return null; 
	    }
	    Address location = address.get(0); 
	    location.getLatitude(); 
	    location.getLongitude(); 
	 
	    return location; 
	}
	/**
	 * Find address from location
	 * @param strAddress
	 * @return
	 */
	public static Address getLocationFromCoordinates(double latitude, double longitude, Context context) {
		Geocoder coder = new Geocoder(context); 
		List<Address> address = null; 
		 
	    try {
			address = coder.getFromLocation(latitude, longitude, 1);
		} catch (IOException e) {
			e.printStackTrace();
		} 
	    if (address == null) { 
	        return null; 
	    }
	    Address location = address.get(0); 
	    location.getLatitude(); 
	    location.getLongitude(); 
	    return location; 
	}
	/**
	 * Init LocationManager
	 */
	public static void initLocationManager(Context context) {
		m_LocationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); 

	    // Define a listener that responds to location updates 
	    m_LocationListenerGPS = new LocationListener() { 
	    	public void onLocationChanged(android.location.Location location) { 
	          // Called when a new location is found by the network location provider. 
	    		for (LocationListener l:listenersGPS) {
	    			l.onLocationChanged(location);
	    		}
	        } 
	
	        public void onStatusChanged(String provider, int status, Bundle extras) {} 
	        public void onProviderEnabled(String provider) {} 
	        public void onProviderDisabled(String provider) {}
	
	    }; 
	}
	/**
	 * GPS provider
	 * @return
	 */
	public static Location getLastKnownPosition() {
		Criteria c = new Criteria(); 
	    c.setAltitudeRequired(false); 
	    c.setBearingRequired(false); 
	    c.setSpeedRequired(false); 
	    c.setCostAllowed(false); 
	    c.setAccuracy(Criteria.ACCURACY_FINE); 
		try {
		    m_BestProvider = m_LocationManager.getBestProvider(c, true); 
			m_Location = m_LocationManager.getLastKnownLocation(m_BestProvider); 
		} catch (Exception e) {
		}
		if (m_BestProvider==null || m_Location==null) {
		    c.setAccuracy(Criteria.ACCURACY_COARSE); 
			try {
			    m_BestProvider = m_LocationManager.getBestProvider(c, true); 
				m_Location = m_LocationManager.getLastKnownLocation(m_BestProvider); 
			} catch (Exception e) {
			}
		}
		if (m_BestProvider==null || m_Location==null) {
		    c.setAccuracy(Criteria.ACCURACY_MEDIUM); 
				try {
				    m_BestProvider = m_LocationManager.getBestProvider(c, true); 
					m_Location = m_LocationManager.getLastKnownLocation(m_BestProvider); 
				} catch (Exception e) {
				}
		}
		if (m_BestProvider==null || m_Location==null) {
		    c.setAccuracy(Criteria.ACCURACY_LOW); 
			try {
			    m_BestProvider = m_LocationManager.getBestProvider(c, false); 
				m_Location = m_LocationManager.getLastKnownLocation(m_BestProvider); 
			} catch (Exception e) {
			}
	}
	    return m_Location;
	}
	
	/**
	 * Get user current location
	 * @return
	 */
	public static android.location.Location getLocation() {
		if (m_Location==null) {
			return null;
		}
	    m_Location = getLastKnownPosition();
		return m_Location;
	}

	public static LocationManager getLocationManager(Context context) {
		if (m_LocationManager==null) {
			initLocationManager(context);
		}
		return m_LocationManager;
	}
	
	public static boolean addLocationListenerGPS(LocationListener l) {
		if (m_LocationManager==null)
			return false;
		if (listenersGPS.size()==0) {
			Runnable r = new Runnable() {
				public void run() {
				    m_LocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 0, m_LocationListenerGPS); 
				    m_LocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 1000, 0, m_LocationListenerGPS); 
				}
			};
			Thread t = new Thread(r);
			t.start();
		}
		if (!listenersGPS.contains(l))
				listenersGPS.add(l);
		return true;
	}
	
	public static boolean removeLocationListenerGPS(LocationListener l) {
		listenersGPS.remove(l);
		if (listenersGPS.size()==0) {
		    m_LocationManager.removeUpdates(m_LocationListenerGPS); 
		}
		return true;
	}

	public static void free() {
		if (m_LocationManager!=null && m_LocationListenerGPS!=null) {
			m_LocationManager.removeUpdates(m_LocationListenerGPS);
			m_LocationManager=null;
			m_LocationListenerGPS=null;
			System.gc();
		}
	}
}
